package be.kuleuven.cs.mop.domain.model.impl;


import static org.junit.Assert.*;

import java.util.ArrayList;
import java.util.Calendar;
import java.util.GregorianCalendar;
import java.util.HashMap;
import java.util.HashSet;

import org.junit.*;

import be.kuleuven.cs.mop.TestEnvironment;
import be.kuleuven.cs.mop.domain.exceptions.BusinessRuleTwoViolationExceptionTask;
import be.kuleuven.cs.mop.domain.exceptions.TaskManagerException;
import be.kuleuven.cs.mop.domain.model.TaskStatus;
import be.kuleuven.cs.mop.domain.model.impl.ResourceImpl;
import be.kuleuven.cs.mop.domain.model.impl.TaskImpl;



public class TaskImplTest {
	
	private static TestEnvironment ENV;
	
	private static ArrayList<ResourceImpl>	resources;
	private static TaskImpl					task1, task2;
	private static HashSet<TaskImpl>		tasks;
	
	
	
	@Before
	public void setUp() throws Exception {
		ENV = TestEnvironment.newInstance();
		
		resources	= new ArrayList<ResourceImpl>();
		tasks		= new HashSet<TaskImpl>();
	}
	
	@BeforeClass
	public static void setUpBeforeClass() throws Exception {}
	
	@After
	public void tearDown() throws Exception {}
	
	@AfterClass
	public static void tearDownAfterClass() throws Exception {}
	
	@Test(expected=TaskManagerException.class)
	public void testConstructorIllegalCase1() throws TaskManagerException{
		new TaskImpl(null,	//	invalid
				new Schedule(ENV.datum1, ENV.datum2, ENV.duration),
				new HashSet<TaskImpl>(),
				new HashMap<String, Field>(),
				ENV.testTaskType);
	}
	
	@Test(expected=NullPointerException.class)
	public void testConstructorIllegalCase2() throws TaskManagerException{
		new TaskImpl(ENV.user1,
				new Schedule(ENV.datum1, ENV.datum2, ENV.duration),
				new HashSet<TaskImpl>(),
				null,	// invalid
				ENV.testTaskType);
	}
	
	@Test(expected=TaskManagerException.class)
	public void testConstructorIllegalCase3() throws TaskManagerException{
		new TaskImpl(ENV.user1,
				new Schedule(ENV.datum1, ENV.datum2, 0L),	//	invalid
				new HashSet<TaskImpl>(),
				new HashMap<String, Field>(),
				ENV.testTaskType);
	}
	
	@Test(expected=TaskManagerException.class)
	public void testConstructorIllegalCase4() throws TaskManagerException {
		new TaskImpl(ENV.user1,
				new Schedule(ENV.datum1, ENV.datum2, ENV.duration),
				new HashSet<TaskImpl>(),
				new HashMap<String, Field>(),
				null);
	}
	
	@Test
	public void testConstructorLegalCase1() throws TaskManagerException {
		task1 = new TaskImpl(ENV.user1,
				new Schedule(ENV.datum1, ENV.datum2, ENV.duration),
				new HashSet<TaskImpl>(),
				new HashMap<String, Field>(),
				ENV.testTaskType);
		task1.setProject(ENV.project1);
		
		assertEquals(task1.getUser(),	ENV.user1);
		assertEquals(task1.getType(),	ENV.testTaskType);
		assertNotNull(task1.getProject());
	}
	
	@Test
	public void testConstructorLegalCase2() throws TaskManagerException {
		task1 = new TaskImpl(ENV.user1,
				new Schedule(ENV.datum1, ENV.datum2, ENV.duration),
				new HashSet<TaskImpl>(),
				new HashMap<String, Field>(),
				ENV.testTaskType);
		
		tasks.add(task1);
		resources.add(ENV.resource1);
		
		task2 = new TaskImpl(ENV.user2,
				new Schedule(ENV.datum1, ENV.datum2, 1100000),
				tasks,
				new HashMap<String, Field>(),
				ENV.testTaskType);
		
		assertEquals(ENV.user1, task1.getUser());
		assertEquals(ENV.user2,	task2.getUser());
		assertTrue(task2.getDependencies().contains(task1));
	}
	
	@Test(expected=TaskManagerException.class)
	public void testModifyMemento() throws TaskManagerException {
		final TaskImpl task = ENV.user1.createTask(
				new Schedule(ENV.datum1, ENV.datum2, ENV.duration),
				new HashSet<TaskImpl>(),
				new HashMap<String, Field>(),
				ENV.testTaskType);
		
		try {
			final Calendar deadline = new GregorianCalendar();
			deadline.setTimeInMillis(ENV.world.getClock().getTime().getTimeInMillis() - 1);
			task.modify(
					new HashMap<String, Field>(),
					new Schedule(ENV.datum3, deadline, ENV.duration),
					task.getDependencies());
		} catch (final BusinessRuleTwoViolationExceptionTask e) {
			assertFalse(true);
		}
	}
	
	@Test
	public void testModifyValid() throws TaskManagerException {
		final TaskImpl task = ENV.user1.createTask(
				new Schedule(ENV.datum1, ENV.datum2, ENV.duration),
				new HashSet<TaskImpl>(),
				new HashMap<String, Field>(),
				ENV.testTaskType);
		
		try {
			task.modify(
					new HashMap<String, Field>(),
					new Schedule(ENV.datum3, ENV.datum2, ENV.duration),
					task.getDependencies());
		} catch (final BusinessRuleTwoViolationExceptionTask e) {
			assertFalse(true);
		}
		
		assertEquals(task.getSchedule().getStartDate(),	ENV.datum3);
	}
	
	@Test(expected=TaskManagerException.class)
	public void testModifyUnmodifiable() throws TaskManagerException {
		final TaskImpl task = ENV.user1.createTask(
				new Schedule(ENV.datum1, ENV.datum2, ENV.duration),
				new HashSet<TaskImpl>(),
				new HashMap<String, Field>(),
				ENV.testTaskType);
		
		try {
			assertTrue(task.getState().isModifiable());
			task.updateTaskStatus(TaskStatus.FAILED);
			assertFalse(task.getState().isModifiable());
			task.modify(null, null, null);
		} catch (final TaskManagerException e) {
			//	expected
			throw e;
		} catch (final Exception e) {
			e.printStackTrace();
			assertFalse(true);
		}
	}
	
	@Test
	public void testOnTimeChanged() {
		task1.onTimeChanged(ENV.world.getClock());
	}
	
	@Test
	public void testRemove() throws TaskManagerException {
		final TaskImpl task = ENV.task1;
		assertTrue(ENV.world.getTasks().contains(task));
		task.remove();
		assertFalse(ENV.world.getTasks().contains(task));
	}
	
	@Test
	public void testRemoveRecursive() throws TaskManagerException {
		final TaskImpl taskA = ENV.user1.createTask(
				new Schedule(ENV.datum1, ENV.datum2, ENV.duration),
				new HashSet<TaskImpl>(),
				new HashMap<String, Field>(),
				ENV.testTaskType);
		
		final HashSet<TaskImpl> dependencies = new HashSet<TaskImpl>();
		dependencies.add(taskA);
		final TaskImpl taskB = ENV.user1.createTask(
				new Schedule(ENV.datum1, ENV.datum2, ENV.duration),
				dependencies,
				new HashMap<String, Field>(),
				ENV.testTaskType);
		
		assertTrue(ENV.world.getTasks().contains(taskA));
		assertTrue(ENV.world.getTasks().contains(taskB));
		taskA.removeRecursive();
		assertFalse(ENV.world.getTasks().contains(taskA));
		assertFalse(ENV.world.getTasks().contains(taskB));
	}
	
	@Test(expected=TaskManagerException.class)
	public void testSetProjectNull() throws TaskManagerException {
		task1.setProject(null);
		assertNull(task1.getProject());
	}
	
	@Test(expected=NullPointerException.class)
	public void testStatusInvalidNull() {
		task1.setStatus(null);
	}
	
	@Test
	public void testValidation() throws TaskManagerException {
		task1 = ENV.user1.createTask(
				new Schedule(ENV.datum1, ENV.datum2, ENV.duration),
				new HashSet<TaskImpl>(),
				new HashMap<String, Field>(),
				ENV.testTaskType);
		
		assertNotNull(task1.toString());
		assertFalse(task1.toString().isEmpty());
		assertNotNull(task1.getSchedule());
		assertNotNull(task1.getState());
		assertNotNull(task1.getStatus());
		assertFalse(task1.isAvailable());
		assertTrue(task1.getDependencies().isEmpty());
	}
	
}